package com.SinaMicrobloggingAPI.Core.Request;

import java.io.DataInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;


import com.SinaMicrobloggingAPI.Configuration.SinaAPIUrlConfig;
import com.SinaMicrobloggingAPI.Core.OauthConstants;
import com.SinaMicrobloggingAPI.Core.Response.IResponse;
import com.SinaMicrobloggingAPI.Util.HttpUtility;
import com.SinaMicrobloggingAPI.Util.OauthParametersComparator;
import com.SinaMicrobloggingAPI.Util.String2;
import com.SinaMicrobloggingAPI.Util.StringBuilder2;

public abstract class RequestBase implements IRequest {

	public static final String OAUTH_VERSION = "1.0a";
	public static final String HMACSHA1_SINGNATURE = "HMAC-SHA1";
	public static final String REQUEST_COUNT = "count";
	public static final String SOURCE = "source";
	protected int count = 20;
	public static final String CALLBACK = "oob";
	protected List<NameValuePair> oauthParamenterModels = new ArrayList<NameValuePair>();
	private String oauthTokenSecret = "";
	private String oauthToken = "";
	protected IResponse response;
	protected String userId;
	protected String password;
	protected String oauthNonce = "";
	protected String oauthTimestamp = "";
	protected String verifier = "";

	public String getVerifier() {
		return verifier;
	}

	public void setVerifier(String verifier) {
		this.verifier = verifier;
	}

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public RequestBase(IResponse response) {
		this.response = response;
	}

	protected String getOauthNonce() {
		if (String2.isNullOrEmpty(oauthNonce)) {
			int i = 123400;
			int j = 9999999 - i;
			Random random = new Random();
			int result = random.nextInt(j) + i;
			oauthNonce = String.valueOf(result);
		}
		return oauthNonce;
	}

	protected String getOauthTimestamp() {
		if (String2.isNullOrEmpty(oauthTimestamp)) {
			TimeZone timeZone = TimeZone.getTimeZone("UTC");
			Calendar calendar = Calendar.getInstance(timeZone);
			long millis = calendar.getTime().getTime();
			long seconds = millis / 1000;
			oauthTimestamp = String.valueOf(seconds);
		}

		return oauthTimestamp;

	}

	protected String getRequestBaseUrl() {
		return SinaAPIUrlConfig.getBaseUrl();
	}

	protected String getRequestUrlWithNoParamenters() {
		String baseUrl = SinaAPIUrlConfig.getBaseUrl();
		String subUrl = SinaAPIUrlConfig.getSinaAPIUrlByName(getRequestName())
				.getUrl();
		return String.format("%s%s", baseUrl, subUrl);
	}

	public String getRequestUrlWithParamenters() {

		String requestUrl = getRequestUrlWithNoParamenters();
		String parameneters = getRequestParamenters();
		String signatureString = generateSignatureString(requestUrl,
				parameneters);
		requestUrl = String.format("%s?%s", requestUrl, parameneters);

		return signatureString == "" ? requestUrl : String
				.format("%s&%s=%s", requestUrl,
						OauthConstants.OAUTH_SIGNATURE_KEY, signatureString);
	}

	protected String generateBaseString(String requestUrl, String parameneters) {
		StringBuilder2 baseStringBuilder = new StringBuilder2();

		baseStringBuilder.appendFormat("%s&", getHttpMethod());
		baseStringBuilder.appendFormat("%s&",
				urlEncodeWithUpperCase(requestUrl));
		baseStringBuilder.appendFormat("%s",
				urlEncodeWithUpperCase(parameneters));

		return baseStringBuilder.toString();
	}

	protected String generateSignatureString(String requestUrl,
			String parameneters) {
		String baseString = generateBaseString(requestUrl, parameneters);
		System.out.println(baseString);
		String consumerSecret = getConsumerSecret();
		String tokenSecret = getOauthTokenSecret();
		String signatureString = String.format("%s&%s",
				urlEncodeWithUpperCase(consumerSecret), tokenSecret == "" ? ""
						: urlEncodeWithUpperCase(tokenSecret));

		byte[] key = null;
		try {
			key = signatureString.getBytes(HTTP.UTF_8);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1");
		Mac mac = null;
		try {
			mac = Mac.getInstance("HmacSHA1");
			mac.init(signingKey);
		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}
		byte[] rawHmac = mac.doFinal(baseString.getBytes());
		String base64String = Base64.encodeBase64String(rawHmac);
		System.out.println(base64String);
		return urlEncodeWithUpperCase(base64String);

	}

	protected String urlEncodeWithUpperCase(String value) {
		return HttpUtility.urlEncodeWithUpperCase(value);
	}

	protected String handingString(String value) {
		if (value.getBytes().length != value.length()) {
			return urlEncodeWithUpperCase(urlEncodeWithUpperCase(value));
		} else {
			return urlEncodeWithLowerCase(urlEncodeWithLowerCase(value));
		}
	}

	protected String urlEncodeWithLowerCase(String value) {
		return HttpUtility.urlEncodeWithLowerCase(value);
	}

	protected HttpMethodEnum getHttpMethod() {
		return HttpMethodEnum.GET;
	}

	protected String getConsumerSecret() {
		return SinaAPIUrlConfig.getAppSecret();
	}

	protected String getConsumerKey() {
		return SinaAPIUrlConfig.getAppKey();
	}

	protected abstract String getRequestName();

	protected String getRequestParamenters() {
		BasicNameValuePair oauth_consumer = new BasicNameValuePair(
				OauthConstants.OAUTH_CONSUMER_KEY, getConsumerKey());
		BasicNameValuePair oauth_nonce = new BasicNameValuePair(
				OauthConstants.OAUTH_NONCE_KEY, getOauthNonce());
		BasicNameValuePair oauth_signature_method = new BasicNameValuePair(
				OauthConstants.OAUTH_SIGNATURE_METHOD_KEY,
				RequestBase.HMACSHA1_SINGNATURE);
		BasicNameValuePair oauth_time_stamp = new BasicNameValuePair(
				OauthConstants.OAUTH_TIMESTAMP_KEY, getOauthTimestamp());
		BasicNameValuePair oauth_version = new BasicNameValuePair(
				OauthConstants.OAUTH_VERSION_KEY, RequestBase.OAUTH_VERSION);

		oauthParamenterModels.add(oauth_consumer);
		oauthParamenterModels.add(oauth_nonce);
		oauthParamenterModels.add(oauth_signature_method);
		oauthParamenterModels.add(oauth_version);
		oauthParamenterModels.add(oauth_time_stamp);

		return buildParamenters();
	}

	protected String buildParamenters() {
		int paramentersLength = oauthParamenterModels.size();
		if (paramentersLength > 0) {

			OauthParametersComparator comparator = new OauthParametersComparator();
			Collections.sort(oauthParamenterModels, comparator);

			StringBuilder2 paramenterBuilder = new StringBuilder2();

			for (int i = 0; i < paramentersLength; i++) {
				paramenterBuilder.appendFormat("%s=%s&", oauthParamenterModels
						.get(i).getName(), oauthParamenterModels.get(i)
						.getValue());
			}

			return paramenterBuilder.killTail().toString();
		}
		return "";
	}

	public String getOauthTokenSecret() {
		return oauthTokenSecret;
	}

	public void setOauthTokenSecret(String oauthTokenSecret) {
		this.oauthTokenSecret = oauthTokenSecret;
	}

	public String getOauthToken() {
		return oauthToken;
	}

	public void setOauthToken(String oauthToken) {
		this.oauthToken = oauthToken;
	}

	@Override
	public IResponse getResponse() {

		return this.getHttpMethod() == HttpMethodEnum.GET ? getGetResponse()
				: getPostResponse();
	}

	protected IResponse getGetResponse() {
		HttpClient httpclient = new DefaultHttpClient();
		String requestUrl = getRequestUrlWithParamenters();
		HttpGet httpget = new HttpGet(requestUrl);
		try {
			HttpResponse httpResponse = httpclient.execute(httpget);
			HttpEntity entity = httpResponse.getEntity();

			if (entity != null) {

				String result = EntityUtils.toString(entity);
				response.ParseResult(result.replaceAll("\r", ""));
				System.out.println(result);
				httpclient.getConnectionManager().shutdown();
			}
		} catch (Exception ex) {
			httpget.abort();
			throw new RuntimeException(ex);
		}

		return response;
	}

	protected IResponse getPostResponse() {
		String requestUrl = getRequestUrlWithNoParamenters();
		String parameneters = getRequestParamenters();
		String signatureString = generateSignatureString(requestUrl,
				parameneters);
		HttpClient httpclient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(requestUrl);
		try {
			parameneters = String.format("%s&%s=%s", parameneters,
					OauthConstants.OAUTH_SIGNATURE_KEY, signatureString);
			HttpEntity postentity = new ByteArrayEntity(parameneters
					.getBytes(HTTP.UTF_8));
			httpPost.setHeader("Content-Type",
					"application/x-www-form-urlencoded");
			httpPost.getParams().setBooleanParameter(
					CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
			httpPost.getParams().setParameter(
					CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.UTF_8);
			httpPost.getParams().setParameter(
					CoreProtocolPNames.HTTP_ELEMENT_CHARSET, HTTP.UTF_8);
			httpPost.setEntity(postentity);
			HttpResponse httpResponse = httpclient.execute(httpPost);
			HttpEntity responseentity = httpResponse.getEntity();
			if (responseentity != null) {
				String result = EntityUtils.toString(responseentity);
				response.ParseResult(result.replaceAll("\r", ""));
				System.out.println(result);
				httpclient.getConnectionManager().shutdown();
			}
		} catch (Exception ex) {
			httpPost.abort();
			throw new RuntimeException(ex);
		}

		return response;
	}
}
